package web_api

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"platon-tools/go-service/common/constant"
	"platon-tools/go-service/common/util"
	go_logger "platon-tools/go-service/go-logger"
	"platon-tools/go-service/module"
	"platon-tools/go-service/module/model"
	"strconv"
	"time"
)

// 报警列表
func (api *HttpApi) MonitorList(c *gin.Context) {

	var err error

	userId, exist := c.Get("login_id")
	if !exist || userId == "" {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1103, constant.ERROR_MESSAGE_1103))
		return
	}

	pageStr := c.Query("page")
	pageSizeStr := c.Query("page_size")
	param := c.Query("params")
	nodePrimaryId := c.Query("node_primary_id")
	orderBy := c.Query("order_by")
	order := c.Query("order")

	page, err := strconv.Atoi(pageStr)
	if err != nil {
		go_logger.Logger.Error(err.Error())
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1003, "page 应为数字"))
		return
	}

	pageSize, err := strconv.Atoi(pageSizeStr)
	if err != nil {
		go_logger.Logger.Error(err.Error())
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1003, "pageSize 应为数字"))
		return
	}

	r := api.service.ModuleByName("MYSQLDB")
	if db, ok := r.(*module.MysqlDB); ok {
		var alertRules []*model.SysAlertRule

		var user model.SysUser
		err = db.Table(model.SysUserTable).Where(map[string]interface{}{"id": userId}).Find(&user).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		var role model.SysRole
		err = db.Table(model.SysRoleTable).Where(map[string]interface{}{"id": user.RoleId}).Find(&role).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		var count int
		var countDb = db.Table(model.SysAlertRuleTable).Where(" node_primary_id in (?)", role.GetNodePerm())
		if len(param) > 0 {
			countDb = countDb.Where("rule_name like ?", "%"+param+"%")
		}
		if len(nodePrimaryId) > 0 {
			countDb = countDb.Where("node_primary_id = ?", nodePrimaryId)
		}
		err = countDb.Count(&count).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		if count > 0 {
			selectDb := db.Table(model.SysAlertRuleTable).Where(" node_primary_id in (?)", role.GetNodePerm())
			if len(param) > 0 {
				selectDb = selectDb.Where("rule_name like ?", "%"+param+"%")
			}
			if len(nodePrimaryId) > 0 {
				selectDb = selectDb.Where("node_primary_id = ?", nodePrimaryId)
			}
			if len(orderBy) > 0 {
				selectDb = selectDb.Order(orderBy + " " + order)
			} else {
				selectDb = selectDb.Order("update_time desc")
			}
			err = selectDb.Limit(pageSize).Offset((page - 1) * pageSize).Find(&alertRules).Error
			if err != nil {
				go_logger.Logger.Error(err.Error())
				c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
				return
			}
		}

		c.JSON(http.StatusOK, util.SuccessResponse(map[string]interface{}{
			"list":  alertRules,
			"count": count,
		}))
		return
	}
}

// 报警详情
func (api *HttpApi) MonitorDetail(c *gin.Context) {

	var err error
	nodePrimaryId := c.Query("node_primary_id")

	r := api.service.ModuleByName("MYSQLDB")
	if db, ok := r.(*module.MysqlDB); ok {
		var alertRule model.SysAlertRule

		err = db.Table(model.SysAlertRuleTable).Where(map[string]interface{}{
			"id": nodePrimaryId,
		}).Find(&alertRule).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		c.JSON(http.StatusOK, util.SuccessResponse(alertRule))
		return
	}
}

type AddMonitorRequest struct {
	NodePrimaryId   int    `json:"node_primary_id" binding:"required"`  // 节点ID-节点表ID
	AlertUserId     int    `json:"alert_user_id" binding:"required"`    // 告警人
	RuleName        string `json:"rule_name" binding:"required"`        // 规则名称
	AnnounceType    int    `json:"announce_type" binding:"required"`    // 通知类型
	AlertType       int    `json:"alert_type" binding:"required"`       // 报警类型
	AlertQuota      string `json:"alert_quota" binding:"required"`      // 告警指标
	TriggerOperator string `json:"trigger_operator" binding:"required"` // 操作符
	TriggerValue    string `json:"trigger_value" binding:"required"`    // 阈值
}

// 新增监控
func (api *HttpApi) AddMonitor(c *gin.Context) {

	var err error

	userId, exist := c.Get("login_id")
	if !exist || userId == "" {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1103, constant.ERROR_MESSAGE_1103))
		return
	}
	userName, exist := c.Get("login_name")
	if !exist || userName == "" {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1103, constant.ERROR_MESSAGE_1103))
		return
	}

	var param AddMonitorRequest
	if err := c.ShouldBindJSON(&param); err != nil {
		go_logger.Logger.Error(err.Error())
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1002, constant.ERROR_MESSAGE_1002))
		return
	}

	r := api.service.ModuleByName("MYSQLDB")
	if db, ok := r.(*module.MysqlDB); ok {
		var alertRule model.SysAlertRule

		alertRule.NodePrimaryId = param.NodePrimaryId
		alertRule.AlertUserId = param.AlertUserId
		alertRule.RuleName = param.RuleName
		alertRule.AnnounceType = param.AnnounceType
		alertRule.AlertType = param.AlertType
		alertRule.AlertQuota = param.AlertQuota
		alertRule.TriggerOperator = param.TriggerOperator
		alertRule.TriggerValue = param.TriggerValue
		alertRule.Status = constant.NODE_STATUS_USING
		alertRule.CreateUserName = util.ToString(userName)
		alertRule.CreateTime = time.Now()
		alertRule.UpdateUserName = util.ToString(userName)
		alertRule.UpdateTime = time.Now()

		err = db.Table(model.SysAlertRuleTable).Save(&alertRule).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		c.JSON(http.StatusOK, util.SuccessResponse(nil))
		return
	}
}

type EditMonitorRequest struct {
	Id              int    `json:"id" binding:"required"`               // 报警主键
	AlertUserId     int    `json:"alert_user_id" binding:"required"`    // 告警人
	AlertType       int    `json:"alert_type" binding:"required"`       // 报警类型
	AlertQuota      string `json:"alert_quota" binding:"required"`      // 告警指标
	TriggerOperator string `json:"trigger_operator" binding:"required"` // 操作符
	TriggerValue    string `json:"trigger_value" binding:"required"`    // 阈值
}

// 编辑监控指标
func (api *HttpApi) EditMonitor(c *gin.Context) {

	var err error

	userId, exist := c.Get("login_id")
	if !exist || userId == "" {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1103, constant.ERROR_MESSAGE_1103))
		return
	}
	userName, exist := c.Get("login_name")
	if !exist || userName == "" {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1103, constant.ERROR_MESSAGE_1103))
		return
	}

	var param EditMonitorRequest
	if err := c.ShouldBindJSON(&param); err != nil {
		go_logger.Logger.Error(err.Error())
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1002, constant.ERROR_MESSAGE_1002))
		return
	}

	r := api.service.ModuleByName("MYSQLDB")
	if db, ok := r.(*module.MysqlDB); ok {
		var alertRule model.SysAlertRule
		where := map[string]interface{}{
			"id": param.Id,
		}
		err = db.Table(model.SysAlertRuleTable).Where(where).First(&alertRule).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		alertRule.AlertType = param.AlertType
		alertRule.AlertQuota = param.AlertQuota
		alertRule.AlertUserId = param.AlertUserId
		alertRule.TriggerOperator = param.TriggerOperator
		alertRule.TriggerValue = param.TriggerValue
		alertRule.UpdateUserName = util.ToString(userName)
		alertRule.UpdateTime = time.Now()

		err = db.Table(model.SysAlertRuleTable).Save(&alertRule).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1002, constant.ERROR_MESSAGE_1002))
			return
		}

		c.JSON(http.StatusOK, util.SuccessResponse(nil))
		return
	}
}

// 禁用监控
func (api *HttpApi) FreezeMonitor(c *gin.Context) {
	var err error

	userId, exist := c.Get("login_id")
	if !exist || userId == "" {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1103, constant.ERROR_MESSAGE_1103))
		return
	}
	userName, exist := c.Get("login_name")
	if !exist || userName == "" {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1103, constant.ERROR_MESSAGE_1103))
		return
	}
	ruleId := c.Query("rule_id")

	r := api.service.ModuleByName("MYSQLDB")
	if db, ok := r.(*module.MysqlDB); ok {
		var alertRule model.SysAlertRule
		where := map[string]interface{}{
			"id": ruleId,
		}
		err = db.Table(model.SysAlertRuleTable).Where(where).First(&alertRule).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		alertRule.Status = constant.USER_STATUS_FREESE
		alertRule.UpdateUserName = util.ToString(userName)
		alertRule.UpdateTime = time.Now()

		err = db.Table(model.SysAlertRuleTable).Save(&alertRule).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1002, constant.ERROR_MESSAGE_1002))
			return
		}

		c.JSON(http.StatusOK, util.SuccessResponse(nil))
		return
	}
}

// 启用监控
func (api *HttpApi) StartMonitor(c *gin.Context) {
	var err error

	userId, exist := c.Get("login_id")
	if !exist || userId == "" {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1103, constant.ERROR_MESSAGE_1103))
		return
	}
	userName, exist := c.Get("login_name")
	if !exist || userName == "" {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1103, constant.ERROR_MESSAGE_1103))
		return
	}
	ruleId := c.Query("rule_id")

	r := api.service.ModuleByName("MYSQLDB")
	if db, ok := r.(*module.MysqlDB); ok {
		var alertRule model.SysAlertRule
		where := map[string]interface{}{
			"id": ruleId,
		}
		err = db.Table(model.SysAlertRuleTable).Where(where).First(&alertRule).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		alertRule.Status = constant.USER_STATUS_USING
		alertRule.UpdateUserName = util.ToString(userName)
		alertRule.UpdateTime = time.Now()

		err = db.Table(model.SysAlertRuleTable).Save(&alertRule).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1002, constant.ERROR_MESSAGE_1002))
			return
		}

		c.JSON(http.StatusOK, util.SuccessResponse(nil))
		return
	}
}

// 节点报警状态汇总
type MonitorTotalResponse struct {
	TotalMonitorNode string `json:"total_staking"`
	HealthyNode      string `json:"healthy_node"`
	WarningNode      string `json:"warning_node"`
	FatalNode        string `json:"fatal_node"`
}

// 节点报警状态汇总
func (api *HttpApi) MonitorNodeTotal(c *gin.Context) {

	var err error

	userId, exist := c.Get("login_id")
	if !exist || userId == "" {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1103, constant.ERROR_MESSAGE_1103))
		return
	}

	r := api.service.ModuleByName("MYSQLDB")
	if db, ok := r.(*module.MysqlDB); ok {
		var result MonitorTotalResponse

		var nodes []*model.SysNodeMonitor

		var user model.SysUser
		err = db.Table(model.SysUserTable).Where(map[string]interface{}{"id": userId}).Find(&user).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		var role model.SysRole
		err = db.Table(model.SysRoleTable).Where(map[string]interface{}{"id": user.RoleId}).Find(&role).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		selectDb := db.Table(model.SysNodeMonitorTable).Where(" node_primary_id in (?)", role.GetNodePerm())
		err = selectDb.Find(&nodes).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		var healthy, warning, fatal int
		for _, node := range nodes {
			switch node.NodeStatus {
			case model.NodeMonitorStatusHealthy:
				healthy += 1
			case model.NodeMonitorStatusWarning:
				warning += 1
			case model.NodeMonitorStatusFatal:
				fatal += 1
			}
		}
		result.TotalMonitorNode = fmt.Sprintf("%d", len(nodes))
		result.HealthyNode = fmt.Sprintf("%d", healthy)
		result.WarningNode = fmt.Sprintf("%d", warning)
		result.FatalNode = fmt.Sprintf("%d", fatal)

		c.JSON(http.StatusOK, util.SuccessResponse(result))
		return
	}
}

type NodeMonitorItem struct {
	Id               int     `json:"id"`
	NodePrimaryId    int     `json:"node_primary_id"` // 节点ID-节点表ID
	NodeId           string  `json:"node_id"`
	NodeName         string  `json:"node_name"`
	StaingAddr       string  `json:"staking_addr"`
	NodeCurrentHigh  uint64  `json:"node_current_high"`  // 当前高度
	TotalCurrentHigh uint64  `json:"total_current_high"` // 全网高度
	RunningTime      float64 `json:"running_time"`       // 节点运行时间 单位h
	NodeStatus       int     `json:"node_status"`
}

// 节点报警详情
func (api *HttpApi) MonitorNodeDetail(c *gin.Context) {

	var err error

	userId, exist := c.Get("login_id")
	if !exist || userId == "" {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1103, constant.ERROR_MESSAGE_1103))
		return
	}

	nodeMonitorId := c.Query("id")

	r := api.service.ModuleByName("MYSQLDB")
	if db, ok := r.(*module.MysqlDB); ok {

		var node model.SysNodeMonitor

		selectDb := db.Table(model.SysNodeMonitorTable).Where(" id = ?", nodeMonitorId)
		err = selectDb.Find(&node).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		var nodeContract model.SysNodeContract
		err = db.Table(model.SysNodeContractTable).Where(" node_primary_id = ?", node.NodePrimaryId).Find(&nodeContract).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		nodeMonitorListItem := NodeMonitorItem{}
		nodeMonitorListItem.Id = node.Id
		nodeMonitorListItem.NodeName = node.NodeName
		nodeMonitorListItem.NodePrimaryId = node.NodePrimaryId
		nodeMonitorListItem.NodeId = node.NodeId
		nodeMonitorListItem.StaingAddr = nodeContract.StakingAddr
		nodeMonitorListItem.NodeCurrentHigh = node.NodeCurrentHigh
		nodeMonitorListItem.TotalCurrentHigh = node.NodeCurrentHigh
		nodeMonitorListItem.NodeStatus = node.NodeStatus
		nodeMonitorListItem.RunningTime = time.Now().Sub(node.CreateTime).Hours()

		c.JSON(http.StatusOK, util.SuccessResponse(nodeMonitorListItem))
		return
	}
}

// 节点报警信息列表
func (api *HttpApi) MonitorNodeList(c *gin.Context) {

	var err error

	userId, exist := c.Get("login_id")
	if !exist || userId == "" {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1103, constant.ERROR_MESSAGE_1103))
		return
	}

	pageStr := c.Query("page")
	pageSizeStr := c.Query("page_size")
	param := c.Query("params")

	page, err := strconv.Atoi(pageStr)
	if err != nil {
		go_logger.Logger.Error(err.Error())
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1003, "page 应为数字"))
		return
	}

	pageSize, err := strconv.Atoi(pageSizeStr)
	if err != nil {
		go_logger.Logger.Error(err.Error())
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1003, "pageSize 应为数字"))
		return
	}

	r := api.service.ModuleByName("MYSQLDB")
	if db, ok := r.(*module.MysqlDB); ok {
		var result []NodeMonitorItem

		var nodes []*model.SysNodeMonitor

		var user model.SysUser
		err = db.Table(model.SysUserTable).Where(map[string]interface{}{"id": userId}).Find(&user).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		var role model.SysRole
		err = db.Table(model.SysRoleTable).Where(map[string]interface{}{"id": user.RoleId}).Find(&role).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		selectDb := db.Table(model.SysNodeMonitorTable).Where(" node_primary_id in (?)", role.GetNodePerm())
		if len(param) > 0 {
			selectDb = selectDb.Where("(node_id = ? or node_name = ?)", param, param)
		}

		var count int
		err = selectDb.Count(&count).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		err = selectDb.Limit(pageSize).Offset((page - 1) * pageSize).Find(&nodes).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		for _, node := range nodes {
			nodeMonitorListItem := NodeMonitorItem{}
			nodeMonitorListItem.Id = node.Id
			nodeMonitorListItem.NodeName = node.NodeName
			nodeMonitorListItem.NodePrimaryId = node.NodePrimaryId
			nodeMonitorListItem.NodeId = node.NodeId
			nodeMonitorListItem.NodeCurrentHigh = node.NodeCurrentHigh
			nodeMonitorListItem.TotalCurrentHigh = node.TotalCurrentHigh
			nodeMonitorListItem.NodeStatus = node.NodeStatus
			nodeMonitorListItem.RunningTime = time.Now().Sub(node.CreateTime).Hours()

			result = append(result, nodeMonitorListItem)
		}

		c.JSON(http.StatusOK, util.SuccessResponse(map[string]interface{}{
			"count": count,
			"list":  result,
		}))
		return
	}
}

// 节点折线图数据，对应指标返回最多十个数据
func (api *HttpApi) MonitorAnalysis(c *gin.Context) {

	var err error

	userId, exist := c.Get("login_id")
	if !exist || userId == "" {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1103, constant.ERROR_MESSAGE_1103))
		return
	}

	nodePrimaryId := c.Query("node_primary_id")
	if len(nodePrimaryId) == 0 {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1003, "node_primary_id缺失"))
		return
	}

	target := c.Query("target")
	if len(target) == 0 {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1003, "target缺失"))
		return
	}

	timeGapStr := c.Query("time_gap")
	if len(timeGapStr) == 0 {
		c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1003, "time_gap缺失"))
		return
	}

	r := api.service.ModuleByName("MYSQLDB")
	if db, ok := r.(*module.MysqlDB); ok {
		result := make([]Point, 0)

		var nodes []*model.SysNodeMonitorHistory

		selectDb := db.Table(model.SysNodeMonitorHistoryTable).Where(" node_primary_id = ?", nodePrimaryId).Limit(5)
		err = selectDb.Find(&nodes).Error
		if err != nil {
			go_logger.Logger.Error(err.Error())
			c.JSON(http.StatusOK, util.FailResponse(constant.ERROR_CODE_1001, constant.ERROR_MESSAGE_1001))
			return
		}

		for _, node := range nodes {
			point := Point{
				X: node.CreateTime.Format(constant.DATE_HOUR_MINUTE),
			}
			switch target {
			case "cpu":
				point.Y = node.CPURatio
			case "memory":
				point.Y = node.MemoryRatio / 1024 / 1024
			case "disk":
				point.Y = node.DiskRatio / 1024 / 1024
			case "io":
				point.Y = node.IORatio
			case "r":
				point.Y = node.RAmount / 1024 / 1024
			case "w":
				point.Y = node.WAmount / 1024 / 1024
			case "rw":
				point.Y = node.RWAmount / 1024 / 1024
			case "rpc":
				point.Y = node.RPCResponseTime
			case "net_work":
				point.Y = node.NetWorkRatio / 1024 / 1024
			}
			result = append(result, point)
		}

		c.JSON(http.StatusOK, util.SuccessResponse(result))
		return
	}
}
